using System;
using System.Threading;
using RxHeat;
using RxHeat.Common;
using RxHeat.NetClassProxy;

namespace RxHeat.Tracing
{
    /// <summary>
    ///     The TraceManager class is responsible for maintaining a list of all TraceListeners, a <see cref="DefaultTracer" /> and a
    ///     <see cref="DefaultFactory" />
    ///     for trace listener.
    ///     Use this class to customize tracing in RxHeat.
    /// </summary>
    public static class TraceManager
    {
        private static readonly ConcurrentDictionary<string, IRxHeatTraceListner> _tracer =
            new ConcurrentDictionary<string, IRxHeatTraceListner>();

        private static readonly Func<string, IRxHeatTraceListner> _defaultTraceListenerFunction =
            s => new DefaultRxHeatTraceListner(s);

        private static readonly ReaderWriterLockSlim TracerLock =
            new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);

        private static Func<string, IRxHeatTraceListner> DefaultFactory { get; set; }

        /// <summary>
        ///     Gets the assigned <see cref="IRxHeatTraceListner" /> tracer object.
        /// </summary>
        /// <value>
        ///     The tracer.
        /// </value>
        public static IRxHeatTraceListner DefaultTracer
        {
            get { return GetTracer("RxHeat"); }
        }

        /// <summary>
        ///     Gets the tracer.
        /// </summary>
        /// <param name="tracerKey">The tracer key.</param>
        /// <returns></returns>
        public static IRxHeatTraceListner GetTracer(string tracerKey)
        {
            return _tracer.GetOrAdd(tracerKey,
                                    (DefaultFactory == null)
                                        ? AssignTraceListenerFactory(_defaultTraceListenerFunction)(tracerKey)
                                        : DefaultFactory(tracerKey));
        }

        /// <summary>
        ///     Assigns the trace listener factory.
        /// </summary>
        /// <param name="createTraceListenerFunction">The function that would create trace listeners for RxHeat.</param>
        public static Func<string, IRxHeatTraceListner> AssignTraceListenerFactory(
            Func<string, IRxHeatTraceListner> createTraceListenerFunction)
        {
            return DefaultFactory = createTraceListenerFunction;
        }

        /// <summary>
        ///     Assigns the default <see cref="DefaultTracer" /> used in RxHeat.
        /// </summary>
        /// <param name="tracer">
        ///     The value of the <see cref="DefaultTracer" />.
        /// </param>
        public static void AssignDefaultTraceListener(IRxHeatTraceListner tracer)
        {
            if (tracer == null)
                return;

            _tracer.AddOrUpdate("RxHeat", x => tracer, (x, u) =>
                {
                    u.Trace(new TraceDetails("TraceManager", Thread.CurrentThread.ToTraceString(),
                                             () =>
                                             string.Format(
                                                 "Replacing RxHeat Default TraceListener {0},{1}",
                                                 u.GetType().DescribeType(),
                                                 tracer.GetType().DescribeType())));
                    return tracer;
                });
        }
    }
}